【S3 Bucketへのアクセス管理】 特定のIAM RoleにのみS3 Bucketへのアクセスを許可し、IAM User GroupとIAM Roleで厳密にスイッチングする方法
はじめに
データアナリティクス事業本部ビッグデータチームのyosh-kです。
今回は以下の要件のアーキテクチャを検討する必要があったので、その一つの構成を検証したいと思います。他にも方法はあると思いますが、現時点ではこれ以外の回答を見つけられていないので、別の良い案が見つかり次第追記したいと思います。
- 背景:
- 特定のS3 Bucketに機密情報などが格納されているため、アクセスできるユーザーを限定したい
- 要件:
- 特定のIAM Roleにアクセス制限されたS3 Bucketを作成したい
- 上記のIAM Roleへは特定のIAM User Groupと特定のIAM Role(
cm-
で始まる)のみがスイッチロールできるようにしたい
結論
- IAM Role - IAM Roleへのスイッチ制限は実現できた
- IAM User Groupに属さないIAM Userからは制限できず、ロールスイッチできる
- AssumeRoleできるポリシーを与えないことで制御する
前提条件
今回は以下の図の構成をCloudFormationで実装していきます。
aws cliコマンドをローカル端末で使用しますので、aws cliコマンドが使用でき、profileの設定も完了している前提で進めます。
また、CM検証用_1
記載のIAM User
とCM検証用_2
記載のIAM Role(cm-yoshiki.kasama)
は既に作成済みであり、スイッチロールもできる状態であることとします。
実装
実装したソースコードはgithub上に残してあるので、全体感を確認したい場合はそちらをご参照ください。
15_s3_bucket_only_group_and_role
以下は実装したフォルダ構成になります。
. ├── README.md ├── cm_kasama_iam_user_groups.yaml ├── cm_kasama_restrict_role.yaml └── cm_kasama_restrict_s3.yaml 0 directories, 4 files
cm_kasama_iam_user_groups.yaml
--- AWSTemplateFormatVersion: '2010-09-09' Resources: KasamaRestrictGroup: Type: "AWS::IAM::Group" Properties: GroupName: "cm-kasama-groups" KasamaRestrictPolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: "cm-kasama-restrict-policy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "sts:AssumeRole" Resource: - !Sub "arn:aws:iam::${AWS::AccountId}:role/cm-kasama-restrict-role" Groups: - Ref: "KasamaRestrictGroup" IAMUser: Type: "AWS::IAM::User" Properties: UserName: "cm-kasama-user" Groups: - Ref: "KasamaRestrictGroup"
cm_kasama_iam_user_groups.yaml
はIAMロール スイッチ元となるCM検証用_2 AWSアカウントのIAM User GroupとGroupに属するIAM Userを作成するyamlファイルになります。許可ポリシーとして、cm-kasama-restrict-role
へのAssumeRoleすることを許可しています。
cm_kasama_restrict_role.yaml
AWSTemplateFormatVersion: "2010-09-09" Description: Creating Role Parameters: Env: Type: String AllowedValues: - dev - prod Default: dev Resources: RestrictIamRole: Type: AWS::IAM::Role Properties: RoleName: "cm-kasama-restrict-role" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: # for IAM User Group - Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Action: "sts:AssumeRole" Condition: Bool: "aws:MultiFactorAuthPresent": "true" ArnLike: "aws:PrincipalArn": !Sub arn:aws:iam::${AWS::AccountId}:user/* # for CM IAM Role - Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${AWS::AccountId}:root Action: "sts:AssumeRole" Condition: ArnLike: "aws:PrincipalArn": !Sub arn:aws:iam::${AWS::AccountId}:role/cm-* Policies: - PolicyName: "S3Access4AssumeRole" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:ListAllMyBuckets Resource: "*" - Effect: Allow Action: - s3:* Resource: - !Sub "arn:aws:s3:::cm-kasama-${Env}-restrict" - !Sub "arn:aws:s3:::cm-kasama-${Env}-restrict/*" Outputs: RoleId: Value: !GetAtt RestrictIamRole.RoleId Export: Name: !Sub Restrict-Iam-Role-Id-${Env}
cm_kasama_restrict_role.yaml
はIAMロール スイッチ先となるCM検証用_2 AWSアカウントのcm-kasama-restrict-role
を作成します。
要点のみ上から見ていきます。
AssumeRolePolicyDocument
- for IAM User Group:
!Sub arn:aws:iam::${AWS::AccountId}:root
と指定することによって、AWS アカウントを指定しています。ConditionArnLike
条件を使用して、該当のAWSアカウントIDに属する全てのIAMユーザーかつ"aws:MultiFactorAuthPresent": "true"
でMFA必須であるもののAssumeRoleを許可しています。 - for CM IAM Role: 先ほどと同様にAWSアカウントを指定し、Condition
ArnLike
条件を使用してcm-
で始まるロールにAssumeRole権限を制限しています。
- for IAM User Group:
- S3Access4AssumeRole:
- 最初のEffectでS3 Bucketの一覧を表示するための
ListAllMyBuckets
を全てのリソースに許可し、次のEffectでcm-kasama-${Env}-restrict
Bucketへの全てのアクションを許可しています。(※1)
- 最初のEffectでS3 Bucketの一覧を表示するための
- Outputs: 作成したIAM RoleのIdはBucket Policyで使用するためExportしています。
※1: ListBucket
のみでコンソール開いたところエラーとなるため、ListAllMyBuckets
としています。
cm_kasama_restrict_s3.yaml
--- AWSTemplateFormatVersion: "2010-09-09" Description: S3 Bucket Parameters: Env: Type: String AllowedValues: - dev - prod Default: dev Resources: CmKasamaRestrictBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub cm-kasama-${Env}-restrict AccessControl: Private BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms VersioningConfiguration: Status: Enabled PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true CmKasamaRestrictBucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: !Ref CmKasamaRestrictBucket PolicyDocument: Version: "2012-10-17" Statement: - Effect: Deny Principal: '*' Action: "s3:*" Resource: - !Sub 'arn:aws:s3:::${CmKasamaRestrictBucket}' - !Sub 'arn:aws:s3:::${CmKasamaRestrictBucket}/*' Condition: StringNotLike: 'aws:userId': !Join ['', [{'Fn::ImportValue': !Sub 'Restrict-Iam-Role-Id-${Env}'}, ':*']] StringNotEquals: "aws:CalledVia": - "cloudformation.amazonaws.com"
cm_kasama_restrict_s3.yaml
はCM検証用_2 AWSアカウントのcm-kasama-restrict-role
のみにアクセス制限されたS3 Bucketを作成します。Bucket自体の設定は今回は省略し、Bucket Policyの設定を確認します。
- CmKasamaRestrictBucketPolicy:
- Effect: Deny設定により、
cm-kasama-${Env}-restrict
Bucketの全てのS3アクションを拒否しています。 - Condition StringNotLike: 上記のDenyに対して、
StringNotLike
により、Restrict-Iam-Role-Id
のみ、アクセスを許可します。 - Condition StringNotEquals: 上記のDenyに対して、
StringNotEquals
により、CloudFormationスタック
からのみ、アクセスを許可します。 - つまり
Restrict-Iam-Role-Id
、CloudFormationスタック
のみアクセスを許可し、他はDenyすることになります。
- Effect: Deny設定により、
デプロイ
それではCloudFormationをそれぞれデプロイしていきたいと思います。
IAM User Group、IAM User
まずはIAM USer GroupとIAM Userをデプロイします。
事前にaws cliコマンドをインストールし、yamlファイルが存在するディレクトリで以下のコマンドを実行します。--profile
オプションについてはCM検証用_2 AWSアカウントのIAM Rolecm-kasama.yoshiki
のprofileを指定します。
aws cloudformation deploy --stack-name cm-kasama-iam-user-groups-dev --template-file ./cm_kasama_iam_user_groups.yaml --no-execute-changeset --profile <roleを作成するアカウントのprofile> --capabilities CAPABILITY_NAMED_IAM
デプロイ後はAWS Management ConsoleのCloudFormation上に該当のスタックに変更セットが作成されるため、変更セットを実行
を押下しデプロイします。
デプロイに完了すると、cm-kasama-groups
とcm-kasama-user
が作成されています。
IAM Role
次にIAM Roleを以下のコマンドでデプロイします。--profile
オプションについては同様に、CM検証用_2 AWSアカウントのIAM Rolecm-kasama.yoshiki
のprofileを指定します。
aws cloudformation deploy --stack-name cm-kasama-restrict-role-dev --template-file ./cm_kasama_restrict_role.yaml --no-execute-changeset --profile <roleを作成するアカウントのprofile> --parameter-overrides Env=dev --capabilities CAPABILITY_NAMED_IAM
デプロイ後はAWS Management ConsoleのCloudFormation上に該当のスタックに変更セットが作成されるため、変更セットを実行
を押下しデプロイします。
デプロイに完了すると、cm-kasama-restrict-role
が作成されています。
S3
最後にS3を以下のコマンドでデプロイします。--profile
オプションについては同様に、CM検証用_2 AWSアカウントのIAM Rolecm-kasama.yoshiki
のprofileを指定します。
aws cloudformation deploy --stack-name cm-kasama-restrict-s3-dev --template-file ./cm_kasama_restrict_s3.yaml --no-execute-changeset --profile ${S3 Bucketを作成するアカウントのprofile} --parameter-overrides Env=dev
デプロイ後はAWS Management ConsoleのCloudFormation上に該当のスタックに変更セットが作成されるため、変更セットを実行
を押下しデプロイします。
デプロイに完了すると、cm-kasama-dev-restrict
が作成されています。
特定のIAM Role以外はS3 Bucketへアクセスできないため、S3 Bucket Policyについては後ほど確認します。
検証
(正常系) IAM User Group内のIAM Userからスイッチロール
先ほど作成したIAM User Groupcm-kasama-groups
のIAM Usercm-kasama-user
からcm-kasama-restrict-role
へスイッチできるか検証します。
まずは作成したIAM Userのコンソールアクセスの有効化とMFAの設定を行います。AWS Management Console→IAM→ユーザー→セキュリティ認証情報→コンソールアクセスを有効にする
を選択します。
作成されたcsvファイルをダウンロードし、その内容をパスワード管理ツールに保存します。私は1Passwordを使用しているので、1Passwordに保存しています。
次にMFAを有効化します。
同じくAWS Management Console→IAM→ユーザー→セキュリティ認証情報→MFAデバイスの割り当て
を選択します。私は1Passowrdを使用しているので、1Passowrdで設定をしています。
割り当て完了しました。
完了後は作成したIAM UserでAWS Management Consoleにログインしていきます。
ログインの前に、後ほど使用するcm-kasama-restrict-role
にコンソールでロールを切り替えるためのリンク
をメモしておきます。
それでは一度ログアウトし、先ほど作成したIAM Userのログイン情報からログインします。
ログイン成功した状態で、先ほどコピーしたリンクへ遷移するとロールスイッチする画面となりますので、任意の表示名を入力し、ロールの切り替え
を押下します。
ロールスイッチ後に該当のS3 Bucketへアクセスすると参照することができました。 Bucket policyも想定通りの内容です。
オブジェクトのアップロードも問題なくできました。
(正常系) IAM Roleからスイッチロール
次にCM検証用_2 AWSアカウントのIAM Rolecm-kasama.yoshiki
からIAM Rolecm-kasama-restrict-role
へとスイッチロールします。AWS Management ConsoleからのIAM RoleからIAM Roleへの切り替えはできないため、AWS CLIからロールスイッチします。以下のブログ記事を参考に設定してみました。
AWS CLIでSwitch Role してさらに Switch Role してみた。(ロールの連鎖:Role chaining)
## ~/.aws/config [default] region = ap-northeast-1 output = json [profile kasama-role] role_arn = arn:aws:iam::<AWS ACCOUNT ID>:role/cm-kasama.yoshiki source_profile = default region = us-east-1 mfa_serial = arn:aws:iam::<AWS ACCOUNT ID>:mfa/cm-kasama.yoshiki [profile kasama-restrict-role] role_arn = arn:aws:iam::<AWS ACCOUNT ID>:role/cm-kasama-restrict-role source_profile = kasama-role
それでは実際に以下のコマンドを試してみます。
aws s3 ls s3://cm-kasama-dev-restrict --profile kasama-restrict-role
正常に参照できました。
(base) kasama.yoshiki@~ % aws s3 ls s3://cm-kasama-dev-restrict --profile kasama-restrict-role 2023-05-04 09:57:32 4 hogehoge.md (base) kasama.yoshiki@~ %
(異常系) IAM User Group外のIAM Userからスイッチロール
今回のcm_kasama_restrict_role.yaml
では、!Sub arn:aws:iam::${AWS::AccountId}:root
に対してsts:AssumeRole
を設定しているので、該当AWSアカウント内かつMFAが有効化、AdministratorAccess
ポリシーなどを持つIAM UserであればIAM Group外でもアクセス可能となります。
検証用のAdministratorAccess
ポリシーを持つ、IAM Userを作成し、コンソールアクセス、MFA有効化させた状態で、ログインしてみます。
先ほどのIAM Userからログインし、ロール履歴からcm-kasama-restrict-role
へスイッチロールします。
想定通りスイッチロールでき、S3 Bucketへのアクセスも可能でした。
(異常系) cm-*で始まらないIAM Roleからスイッチロール
cm-*
で始まらないIAM Role名のIAM Roleからはスイッチロールできない想定なので、検証します。
検証用にAdministratorAccess
と、IAM Userからのsts:AssumeRole
の信頼関係を持つ、IAM Roleを作成します。
先ほどと同様にprofileを設定しコマンドを実行します。
## ~/.aws/config [default] region = ap-northeast-1 output = json [profile kasama-role] role_arn = arn:aws:iam::<AWS ACCOUNT ID>:role/cm-kasama.yoshiki source_profile = default region = us-east-1 mfa_serial = arn:aws:iam::<AWS ACCOUNT ID>:mfa/cm-kasama.yoshiki [profile kasama-restrict-role] role_arn = arn:aws:iam::<AWS ACCOUNT ID>:role/cm-kasama-restrict-role source_profile = kasama-role [profile kasama-yoshiki-test-role] role_arn = arn:aws:iam::<AWS ACCOUNT ID>:role/kasama.yoshiki-test-role source_profile = kasama-role
aws s3 ls s3://cm-kasama-dev-restrict --profile kasama-yoshiki-test-role
想定通りエラーとなり、アクセスできませんでした。
(base) kasama.yoshiki@~ % aws s3 ls s3://cm-kasama-dev-restrict --profile kasama-yoshiki-test-role Enter MFA code for arn:aws:iam::<AWS ACCOUNT ID>:mfa/cm-kasama.yoshiki: An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::<AWS ACCOUNT ID>:assumed-role/cm-kasama.yoshiki/botocore-session-123456 is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::<AWS ACCOUNT ID>:role/kasama.yoshiki-test-role (base) kasama.yoshiki@~ %
参考
- IAM ユーザーグループ
- AWS JSON ポリシーの要素: Principal
- Fn::ImportValue
- AWS CloudFormationで他のスタックからImportValueした文字列をSub関数で結合する
- 特定のバケットまたはフォルダに対する Amazon S3 コンソールのアクセス権をユーザーに付与するにはどうすればよいですか?
- 特定の IAM ロールのみアクセスできる S3 バケットを実装する際に検討したあれこれ
- AWS CLIでSwitch Role してさらに Switch Role してみた。(ロールの連鎖:Role chaining)
- 【IAM】スイッチロールの運用について考えてみた
- aws:PrincipalArn 条件コンテキストキーで IAM グループのプリンシパル ARN を指定できるのか試してみた
- AssumeRole について DiveDeep する
- CloudFormationの全てを味わいつくせ!「AWSの全てをコードで管理する方法〜その理想と現実〜」 #cmdevio
- 1Password で2要素認証のワンタイムパスワードを管理する
最後に
今回の構成の場合、IAM Userのアクセスを厳格に制限できませんでしたが、該当IAM Roleへのスイッチロールの権限がなければ問題ありませんので、ポリシーの制限で実現したいことは可能であると考えています。少々複雑な実装でしたので、他の簡易な実装方法などを思いついたら別途検証していきたいと思います。